import socket
import threading
import queue
import json  # json.dumps(some)打包   json.loads(some)解包
import os
import os.path
import sys
import tkinter
import tkinter.messagebox
import sqlite3
import base64

IP = '127.0.0.1'
PORT = 8080  # 端口
messages = queue.Queue()
users = []  # 0:userName 1:connection
lock = threading.Lock()


def sqlite3():
    conn = sqlite3.connect('chat.db')
    # 创建一个名为"chat_data"的表格
    cursor = conn.cursor()
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS users (
            name TEXT,
            message TEXT 
        )
    ''')
    conn.commit()
    # 插入信息
    cursor.execute("INSERT INTO users (name, message) VALUES (?, ?)", ('A', 'hello'))
    conn.commit()
    # 查询所有信息
    cursor.execute("SELECT * FROM chat_data")
    users = cursor.fetchall()

    for user in users:
        print(user)
        # 更新信息
        cursor.execute("UPDATE chat_data SET message = ? WHERE name = ?", ('hello', 'A'))
        conn.commit()
        try:
            conn = sqlite3.connect('chat.db')
            # 数据库操作
        except sqlite3.Error as e:
            print("SQLite error:", e)
        finally:
            conn.close()
            # 在"users"表格的"name"字段上创建索引
            cursor.execute("CREATE INDEX IF NOT EXISTS idx_name ON users (name)")
            conn.commit()
            conn.close


def Online():  # 统计当前在线人员
    online = []
    for i in range(len(users)):
        online.append(users[i][0]) # 从 users 列表中的每个元素中取出第一个元素（即用户名）添加到 online 列表中。
    return online

def Load(data, addr):
    # 获取锁
    # 当多个线程同时执行lock.acquire()时,只有一个线程能成功地获取锁,然后继续执行代码,其他线程就继续等待直到获得锁为止
    lock.acquire()
    try:
        messages.put((addr, data))
    finally:
        # 释放锁
        # 获得锁的线程用完后一定要释放锁lock.release(),否则等待锁的线程将永远等待下去
        lock.release()


# 接受来自客户端的用户名,如果用户名为空,使用用户的IP与端口作为用户名,如果用户名出现重复,则在出现的用户名依此加上后缀"2","3","4"……
def receive(conn, addr):  # 接收消息
    # recv:接受套接字的数据,数据以字符串形式返回
    user = conn.recv(1024)  # 用户名称
    user = user.decode()
    if user == '用户名不存在':
        user = addr[0] + ':' + str(addr[1])
    # 处理重名用户
    tag = 1
    temp = user
    for i in range(len(users)):  # 检验重名,则在重名用户后加数字
        if users[i][0] == user:
            tag = tag + 1
            user = temp + str(tag)
    # 将处理后的用户名和客户端套接字连接添加到 users 列表中
    users.append((user, conn))
    # 调用 Online 函数获取在线用户列表，并通过 Load 函数发送给用户列表
    USERS = Online()
    Load(USERS, addr)

    # 在获取用户名后便会不断地接受用户端发来的消息(即聊天内容),结束后关闭连接
    try:
        while True:
            # 接收到的字节数据解码为原始的字节数据,用decode方法将字节数据解码为字符串
            data = conn.recv(1024)
            message = base64.b64decode(data).decode()
            message = user + ':' + message
            # 接着，将用户名和消息组合起来，并通过 Load 函数发送到其他客户端。
            Load(message, addr)
        # close:关闭套接字
        conn.close()

    # 如果用户断开连接,将该用户从用户列表中删除,然后更新用户列表
    except:
        j = 0  # 用户断开连接
        for man in users:
            if man[0] == user:
                users.pop(j)  # 服务器端删除退出的用户
                break
            j = j + 1
        USERS = Online()
        Load(USERS, addr)
        conn.close()


# 服务端在接受到数据后,会对其进行一些处理然后发送给客户端,对于聊天内容,服务端直接发送给客户端,而对于用户列表,便由json.dumps处理后发送
def sendData():  # 发送数据
     while True:
        if not messages.empty():
            message = messages.get()
            if isinstance(message[1], str):
                for i in range(len(users)):
                    data = ' ' + message[1]
                    # send:将string中的数据发送到连接的套接字,返回值是要发送的字节数量
                    users[i][1].send(data.encode())
                    print(data)
                    print('\n')

            if isinstance(message[1], list):
                data = json.dumps(message[1])
                for i in range(len(users)):
                    # noinspection PyBroadException
                    try:
                        # send:将string中的数据发送到连接的套接字,返回值是要发送的字节数量
                        users[i][1].send(data.encode())
                    except:
                        pass


class ChatServer(threading.Thread):
    global users, que, lock

    def __init__(self):  # 构造函数
        threading.Thread.__init__(self)
        # 创建一个新的套接字对象，用于IPv4和TCP连接。
        self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    def run(self):
        # 将套接字绑定到一个特定的IP地址和端口
        self.s.bind((IP, PORT))
        # 开始监听连接，并设置最大挂起连接数为5。
        self.s.listen(5)
        q = threading.Thread(target=sendData)
        q.start()
        while True:
            # 接受一个新的连接请求，并返回一个新的套接字对象conn和客户端的地址addr
            conn, addr = self.s.accept()
            t = threading.Thread(target=receive, args=(conn, addr))
            t.start()
        # self.s.close()


def Start():
    tkinter.messagebox.showinfo("提示", "启动成功！")
    server = ChatServer()
    server.setDaemon(True)
    server.start()


root = tkinter.Tk()
root.geometry("200x200")
root.title("PORT")
root.resizable(False, False)
one = tkinter.Label(root, width=200, height=100, background="LightBlue")
one.pack()

startButton = tkinter.Button(root, text="START", command=Start, background="Red")
startButton.place(x=50, y=50, width=100, height=50)


def Exit():
    response = tkinter.messagebox.askyesno("退出", "你确定要退出程序吗？")
    if response:
        tkinter.messagebox.showinfo("提示", "退出成功！")
        root.destroy()
        exit(0)


root.protocol("WM_DELETE_WINDOW", Exit)

exitButton = tkinter.Button(root, text="EXIT",
                            command=lambda: (tkinter.messagebox.showinfo("提示", "退出成功！"), root.destroy(), exit(0)),
                            background="Grey")
exitButton.place(x=50, y=100, width=100, height=50)
exitButton.bind('<Return>', lambda: (tkinter.messagebox.showinfo("提示", "退出成功！"), root.destroy(), exit(0)))

root.mainloop()